/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package L2win;

import PCFGRuleManager.PCFGParsingRule;
import PCFGRuleManager.PCFGParsingRuleProcessor;
import Unit.Element;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

/**
 *
 * @author tuyentran
 */
public class L2win {

    private HashMap<Integer, ArrayList<Element>> startMap;
    private HashMap<Integer, ArrayList<Element>> endMap;
    private ArrayList<Element> chart;
    private Element cand;
    private ArrayList<ArrayList<Element>> leftChains = new ArrayList<ArrayList<Element>>();
    private ArrayList<ArrayList<Element>> rightChains = new ArrayList<ArrayList<Element>>();
    private ArrayList<ArrayList<ArrayList<Element>>> leftChainGroup = new ArrayList<ArrayList<ArrayList<Element>>>();
    private ArrayList<ArrayList<ArrayList<Element>>> rightChainGroup = new ArrayList<ArrayList<ArrayList<Element>>>();
    private ArrayList<Element> formedElements = new ArrayList<Element>();
    private PCFGParsingRuleProcessor pprp;

    public L2win() throws IOException, ClassNotFoundException {
        startMap = new HashMap<Integer, ArrayList<Element>>();
        endMap = new HashMap<Integer, ArrayList<Element>>();
        pprp = new PCFGParsingRuleProcessor();
    }

    /**
     * @function put elem into start map
     *
     * @param elem
     */
    private void putStart(Element elem) {
        if (elem.getStart() >= cand.getEnd()) {
            int start = elem.getStart();
            if (startMap.containsKey(start)) {
                startMap.get(start).add(elem);
            } else {
                ArrayList<Element> startArr = new ArrayList<Element>();
                startArr.add(elem);
                startMap.put(start, startArr);
            }
        }
    }

    /**
     * @function put elem into end map
     *
     * @param elem
     */
    private void putEnd(Element elem) {
        if (elem.getEnd() <= cand.getStart()) {
            int end = elem.getEnd();
            if (endMap.containsKey(end)) {
                endMap.get(end).add(elem);
            } else {
                ArrayList<Element> endArr = new ArrayList<Element>();
                endArr.add(elem);
                endMap.put(end, endArr);
            }
        }
    }

    /**
     * @function create start and end map
     */
    private void createStartEndMap() {
        for (int i = 0; i < chart.size(); i++) {
            Element e = chart.get(i);
            putStart(e);
            putEnd(e);
        }
    }
    
    /**
     * @function create leftChains use endMap
     *
     * @param e
     */
    private void createLeftChains(L2winChainNode node) {
        Element e = node.getCore();
        ArrayList<Element> corChain = node.getCorChain();
        //get the set of elements which have end = start of e
        ArrayList<Element> leftAd = endMap.get(e.getStart());
        if (leftAd != null) {
            for (int i = 0; i < leftAd.size(); i++) {
                Element elem = leftAd.get(i);
                ArrayList<Element> lChain = new ArrayList<Element>();
                lChain.add(elem);
                lChain.addAll(corChain);
                leftChains.add(lChain);
                L2winChainNode adNode = new L2winChainNode(elem, lChain);
                createLeftChains(adNode);
            }
        }
    }

    /**
     * @function create rightChains use endMap
     * @param e
     */
    private void createRightChains(L2winChainNode node) {
        Element e = node.getCore();
        ArrayList<Element> corChain = node.getCorChain();
        //get the set of elements which have start = end of e
        ArrayList<Element> rightAd = startMap.get(e.getEnd());
        if (rightAd != null) {
            for (int i = 0; i < rightAd.size(); i++) {
                Element elem = rightAd.get(i);
                ArrayList<Element> rChain = new ArrayList<Element>();
                rChain.addAll(corChain);
                rChain.add(elem);
                rightChains.add(rChain);
                L2winChainNode adNode = new L2winChainNode(elem, rChain);
                createRightChains(adNode);
            }
        }
    }

    /**
     * @function add new Elements formed by chain to formedElements
     *
     * @param chain
     * @param start
     * @param end
     * @param inside
     */
    private void addFormedElements(String chain, int start, int end, double inside) {
        ArrayList<PCFGParsingRule> rules = getPprp().getByRightSide(chain);// get set of rule which have right side equaled to chain
        if (rules != null) {
            for (int k = 0; k < rules.size(); k++) {
                PCFGParsingRule pRule = rules.get(k);
                Element e = new Element(pRule.getLeftSide(), start, end);
                e.setInside(inside + pRule.getLeftProbability());
                formedElements.add(e); // add new formed Elements
            }
        }
    }

    /**
     * @return created chains
     */
    private ArrayList<ArrayList<Element>> createdChains() {
        startMap.clear();
        endMap.clear();
        formedElements.clear();
        leftChains.clear();
        rightChains.clear();

        createStartEndMap();

        ArrayList<Element> rootChainNode = new ArrayList<Element>();
        L2winChainNode root = new L2winChainNode(this.cand, rootChainNode);

        // <editor-fold desc="create left chain">
        createLeftChains(root);
        // </editor-fold>

        // <editor-fold desc="create right chain">
        createRightChains(root);
        // </editor-fold>

        // <editor-fold desc="use leftChains and rightChains to create chains and use chains to form new Element">
        ArrayList<ArrayList<Element>> chains = new ArrayList<ArrayList<Element>>();

        // <editor-fold desc="all the chains created by leftChains and cand">
        for (int i = 0; i < leftChains.size(); i++) {
            ArrayList<Element> lChain = new ArrayList<Element>();
            ArrayList<Element> leftArr = leftChains.get(i);
            lChain.addAll(leftArr);
            lChain.add(cand);
            chains.add(lChain);
        }
        // </editor-fold>

        // <editor-fold desc="all the chains created by rightChains and cand">
        for (int i = 0; i < rightChains.size(); i++) {
            ArrayList<Element> rChain = new ArrayList<Element>();
            ArrayList<Element> rightArr = rightChains.get(i);
            rChain.add(cand);
            rChain.addAll(rightArr);
            chains.add(rChain);
        }
        // </editor-fold>

        // <editor-fold desc="all the chains created by rightChains, leftChains and cand">
        if ((!leftChains.isEmpty()) && (!rightChains.isEmpty())) {
            for (int i = 0; i < leftChains.size(); i++) {
                ArrayList<Element> leftArr = leftChains.get(i);
                for (int j = 0; j < rightChains.size(); j++) {
                    ArrayList<Element> rightArr = rightChains.get(j);
                    ArrayList<Element> chain = new ArrayList<Element>();
                    chain.addAll(leftArr);
                    chain.add(cand);
                    chain.addAll(rightArr);
                    chains.add(chain);
                }
            }
        }
        // </editor-fold>
        
        return chains;
    }

    /**
     * @function create new Element using chains
     */
    private void createFormedElements() {
        // <editor-fold desc="use leftChains and rightChains to create chains and use chains to form new Element">
        ArrayList<ArrayList<Element>> chains = createdChains();
        for (int i = 0; i < chains.size(); i++) {
            // <editor-fold desc="form new Element">
            ArrayList<Element> chain = chains.get(i);
            String rule = "";
            double subInside = 0;
            for (int j = 0; j < chain.size(); j++) {
                Element e = chain.get(j);
                //System.out.print(e.getLabel()+"["+e.getStart()+","+e.getEnd()+"]");
                rule = rule.concat(e.getLabel() + " ");
                subInside += e.getInside();
            }
            //System.out.println(rule.trim());
            ArrayList<PCFGParsingRule> rules = getPprp().getByRightSide(rule.trim());
            if (rules != null) {
                for (int j = 0; j < rules.size(); j++) {
                    PCFGParsingRule iRule = rules.get(j);
                    //System.out.println(iRule.getLeftSide()+" ==> "+iRule.getRightSide());
                    double inside = iRule.getLeftProbability() + subInside;
                    Element elem = new Element(iRule.getLeftSide(), chain.get(0).getStart(), chain.get(chain.size() - 1).getEnd());
                    elem.setInside(inside);
                    elem.setChildren(chain);
                    formedElements.add(elem);
                }
            }
            // </editor-fold>
        }
        // </editor-fold>
    }

    /**
     * @return the formedElements
     */
    public ArrayList<Element> getFormedElements(Element cand, ArrayList<Element> chart) {
        this.cand = cand;
        this.chart = chart;
        createFormedElements();
        return formedElements;
    }

    /**
     * @return the pprp
     */
    public PCFGParsingRuleProcessor getPprp() {
        return pprp;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //NP[1,2]  A[2,3] V[3,4] R[4,5]  L[5,6] N[6,7] V[7,8]
        //V[8,9]  .[9,10] VP[3,5] WHNP[5,7] VP[7,9] NP[6,8] NP[5,7] 
        //AP[2,4] VP[2,4] NP[5,8] VP[2,5]

        Element cand = new Element("NP", 6, 9);
        ArrayList<Element> chart = new ArrayList<Element>();
        chart.add(new Element("NP", 1, 2));
        chart.add(new Element("A", 2, 3));
        chart.add(new Element("V", 3, 4));
        chart.add(new Element("R", 4, 5));
        chart.add(new Element("L", 5, 6));
        chart.add(new Element("N", 6, 7));
        chart.add(new Element("V", 7, 8));
        chart.add(new Element("V", 8, 9));
        chart.add(new Element(".", 9, 10));
        chart.add(new Element("VP", 3, 5));
        chart.add(new Element("WHNP", 5, 7));
        chart.add(new Element("VP", 7, 9));
        chart.add(new Element("NP", 6, 8));
        chart.add(new Element("NP", 5, 7));
        chart.add(new Element("AP", 2, 4));
        chart.add(new Element("VP", 2, 4));
        chart.add(new Element("NP", 5, 8));
        chart.add(new Element("VP", 2, 5));

        L2win laco = new L2win();
        ArrayList<Element> formedNode = laco.getFormedElements(cand, chart);
        for (int i = 0; i < formedNode.size(); i++) {
            Element e = formedNode.get(i);
            System.out.println(e.getLabel() + "[" + e.getStart() + "," + e.getEnd() + "]");
        }
    }
}
